module("framework.luamono.LuaMonoContainer",package.seeall)

--lua component的容器
--每个类型的component只允许存放一个，如果要放多个，请自行封装
---@class LuaMonoContainer
local LuaMonoContainer = class("LuaMonoContainer")

--GameObject没有active，destroy时不执行container的onDestroy，保底处理一下
local containers_static = {}
function LuaMonoContainer.tryDispose()
    for container, _ in pairs(containers_static) do
        if gohelper.isNil(container._go) then
            if isDebugBuild then
                logWarn("保底 destory: " .. container._path)
            end
            --GC时，保底清除所有userdata的引用
            callWithCatch(container.__onDispose, container)
        end
    end
end

--保底清除userdata引用
function LuaMonoContainer:__onDispose()
    if not containers_static[self] then return end
    containers_static[self] = nil
    if not self._luaMonoList then return end
    for _, comp in ipairs(self._luaMonoList) do
        comp:__onDispose()
    end
    self._monoCom = nil
    self._go = nil
    self._luaMonoList = nil
    self._tempList = nil
    self._hasStarted = false
    self._compDirty = true
end

--创建一个保存所有lua mono 对象的类
--@monoCom 是c#中的LuaMonobehavier
function LuaMonoContainer:ctor(monoCom)
    self._monoCom = monoCom
    self._go = monoCom.gameObject
    self._luaMonoList = {}
    self._compNames = {}
    self._hasStarted = false
    self._compDirty = true

    if isDebugBuild then
        self._path = Framework.GameObjectHelper.GetPath(self._go)
    end
    containers_static[self] = true
end

--获取当前有哪些类型的组件，主要用于编辑器调试显示
function LuaMonoContainer:getCompNames()
    return self._compNames
end

--添加一个lua组件
--如果此类型的com已经有了，则返回已有的comp
function LuaMonoContainer:addCompOnce(clsDefine, ctorParam)
    local comp = self:getComp(clsDefine)
    if nil ~= comp then
        return comp
    end
    comp = clsDefine.New(ctorParam)
    comp:__onInit()
    comp:init(self._go)
    --如果已经started过了，则直接补充一下Start调用
    if self._hasStarted then
        --如果lua comp有定义 onEnable函数，并且当前的lua mono c#组件是enabled的，则补充一下enable调用
        if comp.onEnable and self._monoCom:IsEnabled() then
            comp:onEnable()
        end

        if comp.onStart then
            comp:onStart()
        end
        comp:addEventListeners()
    end

    self._compDirty = true
    table.insert(self._luaMonoList, comp)
    table.insert(self._compNames, comp.__cname)
    return comp
end

--移除某个comp对象
--@ comp 对象
function LuaMonoContainer:removeComp(comp)
    local count = #self._luaMonoList
    local tmpComp = nil
    for idx = count, 1, -1 do
        tmpComp = self._luaMonoList[idx]
        if comp == tmpComp then
            self._compDirty = true
            table.remove(self._luaMonoList, idx)
            table.remove(self._compNames, idx)
            self:_onRemove(tmpComp)
            break
        end
    end
end

--根据类型定义删除这个comp
--@ clsDefine 组件定义类 由于只存在一个，只要找到一个此类型的移除就好
function LuaMonoContainer:removeCompByDefine(clsDefine)
    local count = #self._luaMonoList
    local tmpComp = nil
    for idx = count, 1, -1 do
        tmpComp = self._luaMonoList[idx]
        if isTypeOf(tmpComp, clsDefine) then
            self._compDirty = true
            -- logNormal("rmove lua comp name = "..clsDefine.__cname)
            table.remove(self._luaMonoList, idx)
            table.remove(self._compNames, idx)
            self:_onRemove(tmpComp)
            break
        end
    end
end

--如果移除某个comp，则补充一下调用disable和destroy
function LuaMonoContainer:_onRemove(comp)
    if comp.onDisable then
        comp:onDisable()
    end

    if comp.removeEventListeners then
        comp:removeEventListeners()
    end

    if comp.onDestroy then
        comp:onDestroy()
    end

    comp:__onDispose()
end

--获取某个类型的comp
--@ clsDefine 类型定义
function LuaMonoContainer:getComp(clsDefine)
    for _, comp in ipairs(self._luaMonoList) do
        if isTypeOf(comp, clsDefine) then
            return comp
        end
    end
    return nil
end

function LuaMonoContainer:onEnable()
    -- logNormal(" LuaMonoContainer onEnable ")
    local tempTable = self:_getCompListTemp()

    for _, comp in ipairs(tempTable) do
        if comp.onEnable then
            comp:onEnable()
        end
    end
    tempTable = nil
end

function LuaMonoContainer:onDisable()
    -- logNormal(" LuaMonoContainer onDisable ")
    local tempTable = self:_getCompListTemp()

    for _, comp in ipairs(tempTable) do
        if comp.onDisable then
            comp:onDisable()
        end
    end
    tempTable = nil
end


function LuaMonoContainer:onStart()
    -- logNormal(" LuaMonoContainer onStart ")
    self._hasStarted = true
    local tempTable = self:_getCompListTemp()

    for _, comp in ipairs(tempTable) do
        if comp.onStart then
            comp:onStart()
        end
        if comp.addEventListeners then
            comp:addEventListeners()
        end
    end
    tempTable = nil
end

function LuaMonoContainer:onUpdate()
    -- logNormal(" LuaMonoContainer onUpdate ")
    local tempTable = self:_getCompListTemp()

    for _, comp in ipairs(tempTable) do
        if comp.onUpdate then
            comp:onUpdate()
        end
    end
    tempTable = nil
end

function LuaMonoContainer:onDestroy()
    if not containers_static[self] then return end
    containers_static[self] = nil

    -- logNormal(" LuaMonoContainer onDestroy ")
    local tempTable = self:_getCompListTemp()

    for _, comp in ipairs(tempTable) do
        self:_onRemove(comp)
    end
    tempTable = nil

    self._monoCom = nil
    self._go = nil
    self._luaMonoList = nil
    self._tempList = nil
    self._hasStarted = false
    self._compDirty = true
end

function LuaMonoContainer:onTriggerEnter(other)
    -- logNormal(" LuaMonoContainer onTriggerEnter ")
    local tempTable = self:_getCompListTemp()

    for _, comp in ipairs(tempTable) do
        if comp.onTriggerEnter then
            comp:onTriggerEnter(other)
        end
    end
    tempTable = nil
end

function LuaMonoContainer:onTriggerExit(other)
    -- logNormal(" LuaMonoContainer onTriggerExit ")
    local tempTable = self:_getCompListTemp()

    for _, comp in ipairs(tempTable) do
        if comp.onTriggerExit then
            comp:onTriggerExit(other)
        end
    end
    tempTable = nil
end

function LuaMonoContainer:_getCompListTemp()
    if self._compDirty then
        self._tempList = self._tempList or {}
        --将所有组件挪到临时列表中，再遍历，避免遍历过程中组件被移除，导致数组长度发生变化
        for i, comp in ipairs(self._luaMonoList) do
            self._tempList[i] = comp
        end
        for i = #self._luaMonoList + 1, #self._tempList do
            self._tempList[i] = nil
        end
        self._compDirty = false
    end
    return self._tempList
end

return LuaMonoContainer
